package es.jonatantierno.scrumdailytimer; import roboguice.fragment.RoboFragment; import roboguice.inject.InjectView; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Vibrator; import android.support.v4.view.MotionEventCompat; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.SeekBar; import android.widget.TextView; import com.google.inject.Inject; /** * Timer to use in a Scrum Daily Meeting. Main Screen. */ public class ChronoFragment extends RoboFragment implements ChronoInterface { public static final String TOTAL_TIME = "TOTAL_TIME"; public static final String TIMEOUTS = "TIMEOUTS"; public static final String WARMUP_TIME = "WARMUP_TIME"; public static final String PREFS_NAME = "CHRONO_PREFERENCES"; public static final String TIME_SLOT_LENGTH = "TIME_SLOT_LENGTH"; @InjectView(R.id.wholeLayout) private View mWholeLayout; @InjectView(R.id.countDownTextView) private TextView mCountDownTextView; @InjectView(R.id.participantTextView) private TextView mParticipantTextView; @InjectView(R.id.totalTimeTextView) private TextView mTotalTimeTextView; @InjectView(R.id.tapForNextTextView) private TextView mTapForNextTextView; @InjectView(R.id.seekBar1) private SeekBar mSeekBar; @Inject private ScrumTimer mScrumTimer; @Inject SlotSeekBarController mSeekBarController; @Inject private Provider mProvider; private MediaPlayer mAlarmPlayer; private MediaPlayer mTickPlayer; private int mNumberOfParticipants = 1; private int mNumberOfTimeouts = 0; private String mWarmUpTime = "00:00"; private ChronoStatus mStatus = ChronoStatus.STARTED; private Vibrator mVibrator; private boolean mPausedInTimeout = false; /* * Used to reset back press from main activity. */ boolean isBackPressReset = false; private void start() { mVibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE); new Thread() { public void run() { mAlarmPlayer = mProvider.getAlarmPlayer(getActivity()); } }.start(); new Thread() { public void run() { mTickPlayer = mProvider.getTickPlayer(getActivity()); } }.start(); mScrumTimer.configure(this); mSeekBarController.configure(mSeekBar, this); mNumberOfParticipants = 1; mNumberOfTimeouts = 0; mStatus = ChronoStatus.STARTED; mWholeLayout.setBackgroundResource(R.drawable.background_gradient); mTapForNextTextView.setText(R.string.tap_for_first_participant); mScrumTimer.startTimer(); mWholeLayout.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if (mStatus == ChronoStatus.STARTED) { return false; } mWholeLayout.setBackgroundResource(R.drawable.pause_background_gradient); mScrumTimer.pauseCountDown(); // If tick is playing, then we are in timeout. if (mTickPlayer.isPlaying()) { mPausedInTimeout = true; mTickPlayer.pause(); } return false; } }); mWholeLayout.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = MotionEventCompat.getActionMasked(event); switch (action) { case MotionEvent.ACTION_UP: return endPause(); default: break; } return false; } }); mWholeLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mScrumTimer.isCountDownPaused()) { return; } vibrate(); isBackPressReset = true; switch (mStatus) { case STARTED: // Set time and store storeSlotTime(); mScrumTimer.setTimeSlotLength(mSeekBar.getProgress()); mWholeLayout.setBackgroundResource(R.drawable.meeting_background_gradient); mSeekBar.setVisibility(View.GONE); mStatus = ChronoStatus.COUNTDOWN; mWarmUpTime = mScrumTimer.getPrettyTime(); mParticipantTextView.setVisibility(View.VISIBLE); repaintParticipants(); mTapForNextTextView.setText(R.string.tap_for_next); mScrumTimer.resetCountDown(); mCountDownTextView.setText(mScrumTimer.getPrettyCountDown()); break; case COUNTDOWN: if (mTickPlayer.isPlaying()) { mTickPlayer.pause(); } mNumberOfParticipants++; repaintParticipants(); mScrumTimer.resetCountDown(); mCountDownTextView.setText(mScrumTimer.getPrettyCountDown()); resetBackground(); break; default: } } }); } /** * For Testing */ boolean endPause() { if (mScrumTimer.isCountDownPaused()) { // If in timeout, restart tick player and reset timeout background if (mPausedInTimeout) { mTickPlayer.start(); mWholeLayout.setBackgroundResource(R.drawable.timeout_background_gradient); } else { mWholeLayout.setBackgroundResource(R.drawable.meeting_background_gradient); } mPausedInTimeout = false; mScrumTimer.resumeCountDown(); return true; } return false; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.activity_fullscreen, container, false); } @Override public void onStart() { super.onStart(); start(); } @Override public void onResume() { super.onResume(); // Get settings int timeSlotLength = getSharedPreferences().getInt(TIME_SLOT_LENGTH, -1); if (timeSlotLength == -1) { timeSlotLength = SlotSeekBarController.DEFAULT_VALUE; } mScrumTimer.setTimeSlotLength(timeSlotLength); mSeekBar.setVisibility(View.VISIBLE); mParticipantTextView.setVisibility(View.GONE); mSeekBar.setProgress(timeSlotLength); setTime(timeSlotLength); } public int getNumberOfParticipants() { return mNumberOfParticipants; } /** * This time is called by the seekbar when a new value is set. * * @param time time in seconds to show. */ @Override public void setTime(int time) { mCountDownTextView.setText(mScrumTimer.getPrettyTime(time)); } /** * Pause ticking clock sound */ public void pauseTickSound() { if (mTickPlayer.isPlaying()) { mTickPlayer.pause(); } } /** * Sets time for the total daily meeting timer. * * @param string time to show. */ @Override public void setDailyTimer(final String string) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { mTotalTimeTextView.setText(getString(R.string.total_meeting_time) + string); } }); } @Override public void timeOut() { mNumberOfTimeouts++; if (mAlarmPlayer != null) { mAlarmPlayer.start(); } new Thread(new Runnable() { @Override public void run() { if (mTickPlayer != null) { mTickPlayer.start(); } } }).start(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { mWholeLayout.setBackgroundResource(R.drawable.timeout_background_gradient); } }); } @Override public void setCountDown(final String string) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { mCountDownTextView.setText(string); } }); } /** * Set normal background */ public void resetBackground() { mWholeLayout.setBackgroundResource(R.drawable.meeting_background_gradient); } /** * Haptic feedback. For > Honeycomb that would be: * mWholeLayout.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); But seems to subtle. Will use * {@link Vibrator}, and be Gingerbread compatible besides. */ private void vibrate() { if (mVibrator != null) { mVibrator.vibrate(170); } } private void repaintParticipants() { StringBuffer sb = new StringBuffer(getString(R.string.participant)); sb.append(mNumberOfParticipants); mParticipantTextView.setText(sb.toString()); Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.participant_animation); mParticipantTextView.startAnimation(animation); } void storeSlotTime() { Editor editor = getSharedPreferences().edit(); int timeSlotDuration = mSeekBar.getProgress(); editor.putInt(ChronoFragment.TIME_SLOT_LENGTH, timeSlotDuration); editor.commit(); } /** * For testing. * * @return preferences. */ SharedPreferences getSharedPreferences() { return getActivity().getSharedPreferences(PREFS_NAME, 0); } @Override public void onStop() { super.onStop(); mScrumTimer.stopCountDown(); mScrumTimer.stopTimer(); if (mTickPlayer != null) { mTickPlayer.release(); mTickPlayer = null; } if (mAlarmPlayer != null) { mAlarmPlayer.release(); mAlarmPlayer = null; } storeSlotTime(); } public int getNumberOfTimeouts() { return mNumberOfTimeouts; } public String getPreparationTime() { return mWarmUpTime; } } enum ChronoStatus { STARTED, COUNTDOWN, LAST_COUNTDOWN, END; }